package cn.well.cloud.core.auth.filter;

import cn.hutool.core.util.StrUtil;
import cn.stylefeng.roses.core.util.RenderUtil;
import cn.stylefeng.roses.kernel.model.response.ErrorResponseData;
import cn.well.cloud.base.auth.exception.enums.AuthExceptionEnum;
import cn.well.cloud.base.auth.jwt.JwtTokenUtil;
import cn.well.cloud.base.auth.model.LoginUser;
import cn.well.cloud.base.consts.ConstantsContext;
import cn.well.cloud.common.HttpHeader;
import cn.well.cloud.core.auth.cache.impl.DefaultSessionManager;
import cn.well.cloud.exception.enums.BizExceptionEnum;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.SignatureException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 对客户端请求的jwt token验证过滤器
 * 在security中配置
 * 2019年12月23日
 * @author wison
 *
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    private final Log logger = LogFactory.getLog(this.getClass());

    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    @Autowired
    private DefaultSessionManager sessionManager;
    
    private String tokenHead = "Bearer ";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (request.getServletPath().startsWith("/" + "auth")) {
            chain.doFilter(request, response);
            return;
        }
        
        String requestUrl = request.getRequestURI();
        logger.info("requestURL:" + requestUrl);
        
        String authToken = request.getParameter("token");
        String requestHeader = request.getHeader(ConstantsContext.getTokenHeaderName());
        if (StrUtil.isNotBlank(requestHeader) && requestHeader.startsWith(tokenHead)) {
            authToken = requestHeader.substring(tokenHead.length());// "Bearer "之后的内容
        }
        if (StrUtil.isNotBlank(authToken)) {
            try {
                logger.info("token:"+authToken);
                logger.info("token对象:"+ JSON.toJSONString(jwtTokenUtil.getJwtPayLoad(authToken).toMap()));
                Long userId = jwtTokenUtil.getJwtPayLoad(authToken).getUserId();
                logger.info("checking authentication :"+ userId);
                
                if (userId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                    //从已有的user缓存中取了出user信息
                    LoginUser user = sessionManager.getSession(authToken);
                    if(user ==null){
                        //缓存异常,需要重新登陆
                        RenderUtil.renderJson(response,  new ErrorResponseData(AuthExceptionEnum.LOGIN_EXPPIRED.getCode(), AuthExceptionEnum.LOGIN_EXPPIRED.getMessage()));
                        return;   
                    }
                    //验证token是否过期,包含了验证jwt是否正确
                    try {
                        boolean flag = jwtTokenUtil.isTokenExpired(authToken);
                        if (flag) {
                            //token过期
                            RenderUtil.renderJson(response,  new ErrorResponseData(BizExceptionEnum.TOKEN_EXPIRED.getCode(), BizExceptionEnum.TOKEN_EXPIRED.getMessage()));
                            return; 
                        }
                        //创建当前登录上下文
                        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
    
                        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                        
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                        
                    } catch (JwtException e) {
                        logger.error(e.getMessage());
                        //有异常就是token解析失败 
                        RenderUtil.renderJson(response,  new ErrorResponseData(BizExceptionEnum.TOKEN_ERROR.getCode(), BizExceptionEnum.TOKEN_ERROR.getMessage()));
                        return; 
                    }
                } else {
                    //header没有带Bearer字段
                    RenderUtil.renderJson(response,  new ErrorResponseData(BizExceptionEnum.TOKEN_ASSERTED_MISS.getCode(), BizExceptionEnum.TOKEN_ASSERTED_MISS.getMessage()));
                    return; 
                }
            } catch (SignatureException e) {
                RenderUtil.renderJson(response,  new ErrorResponseData(BizExceptionEnum.TOKEN_ASSERTED_MISS.getCode(), BizExceptionEnum.TOKEN_ASSERTED_MISS.getMessage()));
                return; 
            }
        }

        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        HttpHeader header = new HttpHeader();
        Long userId = jwtTokenUtil.getJwtPayLoad(authToken).getUserId();

        header.setToken(authToken);
        header.setUserId(userId);
        header.setIp(ip);
        HttpHeader.set(header);

        chain.doFilter(request, response);
    }
}